use freedesktop_desktop_entry::{DesktopEntry, LocaleMap};

use crate::{AutoLaunch, Result};
use std::{borrow::Cow, fs, io::Write, path::PathBuf};

/// Linux implement
impl AutoLaunch {
    /// Create a new AutoLaunch instance
    /// - `app_name`: application name
    /// - `app_path`: application path
    /// - `args`: startup args passed to the binary
    ///
    /// ## Notes
    ///
    /// The parameters of `AutoLaunch::new` are different on each platform.
    pub fn new(app_name: &str, app_path: &str, args: &[impl AsRef<str>]) -> AutoLaunch {
        AutoLaunch {
            app_name: app_name.into(),
            app_path: app_path.into(),
            args: args.iter().map(|s| s.as_ref().to_string()).collect(),
        }
    }

    /// Enable the AutoLaunch setting
    ///
    /// get_current?
    ///	    no: copy .desktop to ~/.config/autostart, finished
    ///	    yes: is in user cfg?
    ///			no: copy .desktop to ~/.config/autostart, finished
    ///			yes: unset Hidden and X-GNOME-Autostart-enabled
    ///
    /// ## Errors
    ///
    /// - failed to create dir `~/.config/autostart`
    /// - failed to create file `~/.config/autostart/{app_name}.desktop`
    /// - failed to write bytes to the file
    pub fn enable(&self) -> Result<()> {
        let auto_start_path = self.get_file();
        if auto_start_path.exists() {
            // Unset Hidden and X-GNOME-Autostart-enabled
            if let Ok(mut entry) =
                DesktopEntry::from_path(auto_start_path.clone(), Option::<&[&str]>::None)
            {
                let action_key = "Desktop Entry";
                if let Some(keymap) = entry.groups.0.get_mut(action_key) {
                    keymap.0.remove("Hidden");
                    keymap.0.remove("X-GNOME-Autostart-enabled");

                    let mut file = fs::OpenOptions::new()
                        .write(true)
                        .create(true)
                        .truncate(true)
                        .open(auto_start_path.as_path())?;
                    file.write_all(format!("{entry}").as_bytes())?;
                    return Ok(());
                }
            }
        }

        let dir = get_dir();
        if !dir.exists() {
            fs::create_dir_all(&dir).or_else(|e| {
                if e.kind() == std::io::ErrorKind::AlreadyExists {
                    Ok(())
                } else {
                    Err(e)
                }
            })?;
        }

        let mut paths = vec![];

        if let Some(data_home) = dirs::data_dir() {
            paths.push(data_home.join("applications"));
        }
        paths.push(PathBuf::from("/usr/local/share/applications"));
        paths.push(PathBuf::from("/usr/share/applications"));

        for path in paths {
            let file = path.join(format!("{}.desktop", self.app_name));
            if file.exists() {
                fs::copy(file, auto_start_path.as_path())?;
                return Ok(());
            }
        }

        let data = format!(
            "[Desktop Entry]\n\
            Type=Application\n\
            Version=1.0\n\
            Name={}\n\
            Comment={}startup script\n\
            Exec={} {}\n\
            StartupNotify=false\n\
            Terminal=false",
            self.app_name,
            self.app_name,
            self.app_path,
            self.args.join(" ")
        );

        let mut file = fs::OpenOptions::new()
            .write(true)
            .create(true)
            .truncate(true)
            .open(auto_start_path.as_path())?;
        file.write_all(data.as_bytes())?;
        Ok(())
    }

    /// Disable the AutoLaunch setting
    ///
    /// get_current?
    ///		no: disabled already, finished
    ///		yes: unset X-GNOME-Autostart-enabled, set Hidden=true
    ///
    /// ## Errors
    ///
    /// - failed to remove file `~/.config/autostart/{app_name}.desktop`
    pub fn disable(&self) -> Result<()> {
        let file = self.get_file();
        if file.exists() {
            // Set Hidden=true
            if let Ok(mut entry) = DesktopEntry::from_path(file.clone(), Option::<&[&str]>::None) {
                let action_key = "Desktop Entry";
                if let Some(keymap) = entry.groups.0.get_mut(action_key) {
                    let key = "Hidden".to_string();
                    let value = ("true".to_string(), LocaleMap::default());
                    keymap.0.insert(key, value);
                    keymap.0.remove("X-GNOME-Autostart-enabled");

                    let mut file = fs::OpenOptions::new()
                        .write(true)
                        .create(true)
                        .truncate(true)
                        .open(file)?;
                    file.write_all(format!("{entry}").as_bytes())?;
                    return Ok(());
                }
            }
        }
        Ok(())
    }

    /// Check whether the AutoLaunch setting is enabled
    pub fn is_enabled(&self) -> Result<bool> {
        let file = self.get_file();
        if !file.exists() {
            return Ok(false);
        }
        if let Ok(entry) = DesktopEntry::from_path(file.clone(), Option::<&[&str]>::None) {
            let disabled = entry.desktop_entry("Hidden").map_or(false, |v| v == "true")
                || entry
                    .desktop_entry("X-GNOME-Autostart-enabled")
                    .map_or(false, |v| v == "true");
            return Ok(!disabled);
        }

        Ok(true)
    }

    /// Get the desktop entry file path
    fn get_file(&self) -> PathBuf {
        get_dir().join(format!("{}.desktop", self.app_name))
    }
}

/// Get the autostart dir
fn get_dir() -> PathBuf {
    dirs::home_dir().unwrap().join(".config").join("autostart")
}
